﻿#include <stdlib.h>
#include <Windows.h>
#include <direct.h>
#include <string>

#include <windows.storage.h>
#include <wrl.h>

#undef min
#undef max
#include <fast_io/fast_io.h>
#include <fast_io/fast_io_freestanding.h>
#include "Utils.h"

const std::u8string Utils::Utils::RoamingStateFold = Utils::Utils::GetRoamingFolderPath();
// Minecraft Module
const uintptr_t Utils::Utils::CachedBase = reinterpret_cast<uintptr_t>(GetModuleHandleW(L"Minecraft.Windows.exe"));
MODULEINFO Utils::Utils::Minecraft_MODULEINFO{};
// Helium Module
HMODULE Utils::Utils::helium_HMODULE{};
LPVOID Utils::Utils::helium_LPVOID{};
MODULEINFO Utils::Utils::helium_MODULEINFO{};

fast_io::net_service Utils::Utils::service{};

_NODISCARD uintptr_t Utils::Utils::getBase() noexcept {
	return CachedBase;
}

// func
std::u8string Utils::Utils::Base64Encode(const std::u8string_view in) noexcept {
	std::u8string out{};

	uint32_t val = 0ui32;
	int32_t valb = -6;
	for (uint8_t c : in) {
		val = (val << 8) + c;
		valb += 8;
		while (valb >= 0) {
			out.push_back(base64_imme::Base64Alphabet[(val >> valb) & 0x3F]);
			valb -= 6;
		}
	}
	if (valb > -6) 
		out.push_back(base64_imme::Base64Alphabet[((val << 8) >> (valb + 8)) & 0x3F]);
	while (out.size() % 4) 
		out.push_back(u8'=');
	return out;
}

std::u8string Utils::Utils::Base64Decode(const std::u8string_view in) noexcept {
	std::u8string out{};

	constexpr auto T = base64_imme::Base64DecodeTable();

	uint32_t val = 0ui32;
	int32_t valb = -8;
	for (uint8_t c : in) {
		if (T[c] == 255ui8) [[unlikely]]
			break;
		val = (val << 6) + T[c];
		valb += 6;
		if (valb >= 0) {
			out.push_back(static_cast<char>((val >> valb) & 0xFF));
			valb -= 8;
		}
	}
	return out;
}

std::u8string Utils::Utils::RandomString(const size_t size) noexcept {
	constexpr std::u8string_view str{u8"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"};
	std::u8string res{};
	res.reserve(size);
	for (size_t i = 0; i < size; ++i) 
		res.push_back(str[rand() % str.length()]);
	return res;
}

std::u8string Utils::Utils::GetClipboardText() noexcept {
	if (!OpenClipboard(NULL)) [[unlikely]] 
		return std::u8string{};
	else [[likely]] {
		HANDLE hData = GetClipboardData(CF_UNICODETEXT);
		char8_t* pszText = static_cast<char8_t*>(GlobalLock(hData));
		if (pszText == nullptr)
			return std::u8string{};
		CloseClipboard();
		//return std::u8string(pszText);
		return fast_io::u8concat(fast_io::mnp::os_c_str(pszText));
	}
}

void Utils::Utils::SetClipboardText(const std::u8string_view text) noexcept {
	if (!OpenClipboard(NULL)) [[unlikely]] 
		return;
	EmptyClipboard();
	HGLOBAL hg = GlobalAlloc(GMEM_MOVEABLE, text.size() + 1);
	if (!hg) [[unlikely]] {
		CloseClipboard();
		return;
	}
	if (const auto i = GlobalLock(hg); i) [[likely]]
		fast_io::freestanding::my_memcpy(i, text.data(), text.size() + 1);
	GlobalUnlock(hg);
	SetClipboardData(CF_TEXT, hg);
	CloseClipboard();
	GlobalFree(hg);
}

::fast_io::native_file_loader Utils::Utils::ReadFileContents(const std::u8string_view filePath) {
	return ::fast_io::native_file_loader(filePath);
}

#pragma comment(lib, "winmm.lib") // PlaysoundA

void Utils::Utils::SystemPlaySound(std::u8string_view name) noexcept {
	static std::wstring path = fast_io::wconcat(fast_io::mnp::code_cvt(RoamingStateFold), L"resource\\wav\\", fast_io::mnp::code_cvt(name));
	PlaySoundW(path.c_str(), nullptr, SND_FILENAME | SND_ASYNC);
}

#pragma comment(lib, "runtimeobject")

std::u8string Utils::Utils::GetRoamingFolderPath() noexcept {
	using namespace ABI::Windows::Storage;
	using namespace Microsoft::WRL;
	using namespace Microsoft::WRL::Wrappers;

	ComPtr<IApplicationDataStatics> appDataStatics;
	auto hr = RoGetActivationFactory(HStringReference(L"Windows.Storage.ApplicationData").Get(), __uuidof(appDataStatics), &appDataStatics);

	ComPtr<IApplicationData> appData;
	hr = appDataStatics->get_Current(&appData);

	ComPtr<IStorageFolder> roamingFolder;
	hr = appData->get_RoamingFolder(&roamingFolder);

	ComPtr<IStorageItem> folderItem;
	hr = roamingFolder.As(&folderItem);

	HString roamingPathHString;
	hr = folderItem->get_Path(roamingPathHString.GetAddressOf());

	uint32_t pathLength;
	auto roamingPathCStr = roamingPathHString.GetRawBuffer(&pathLength);
	return fast_io::u8concat(fast_io::mnp::code_cvt(std::wstring_view{roamingPathCStr, pathLength}), fast_io::mnp::chvw(u8'\\'), ClientName, fast_io::mnp::chvw(u8'\\'));
}

//void Utils::Utils::MakeFold(const std::u8string_view file)  {
//	CreateDirectoryW(fast_io::wconcat(fast_io::mnp::code_cvt(file)).data(), NULL); 
//}

_NODISCARD std::u8string Utils::Utils::GetLocalTime_string() {
	//char8_t time[50]{};
	//return fast_io::details::print_reserve_iso8601_timestamp_impl(time, GetLocalTime());
	return fast_io::u8concat(local(fast_io::posix_clock_gettime(fast_io::posix_clock_id::realtime)));
}

void Utils::Utils::patchBytes(void* dst, void* src, size_t size) {
	DWORD oldprotect{};
	VirtualProtect(dst, size, PAGE_EXECUTE_READWRITE, &oldprotect);
	fast_io::freestanding::my_memcpy(dst, src, size);
	VirtualProtect(dst, size, oldprotect, &oldprotect);
}

void Utils::Utils::nopBytes(void* dst, size_t size) {
	DWORD oldprotect{};
	VirtualProtect(dst, size, PAGE_EXECUTE_READWRITE, &oldprotect);
	fast_io::freestanding::my_memset(dst, 0x90, size);
	VirtualProtect(dst, size, oldprotect, &oldprotect);
}

std::u8string Utils::Utils::escapeURL(std::u8string_view sv) {
	std::u8string result{};
	for (size_t i = 0; i < sv.size(); i++) {
		char8_t c = sv[i];
		if ((u8'0' <= c && c <= u8'9') ||
			(u8'a' <= c && c <= u8'z') ||
			(u8'A' <= c && c <= u8'Z') ||
			c == u8'/' || c == u8'.') {
			result.push_back(c);
		} else {
			result.push_back(u8'%');
			result.push_back(dec2hexChar(c / 16));
			result.push_back(dec2hexChar(c % 16));
		}
	}
	return result;
}

std::u8string Utils::Utils::deescapeURL(std::u8string_view sv) {
	std::u8string result{};
	for (size_t i = 0; i < sv.size(); i++) {
		char8_t c = sv[i];
		if (c != u8'%') {
			result.push_back(c);
		} else {
			char8_t num = hexChar2dec(sv[i + 1]) * 16 + hexChar2dec(sv[i + 2]);
			result.push_back(num);
			i += 2;
		}
	}
	return result;
}


